import { FixedMath } from './FixedMath';
import { FixedMathLut } from './FixedMathLut';

/**
 * 定点数数学库性能测试
 */
export class FixedMathTest {
    /**
     * 运行所有测试
     */
    public static runAllTests(): void {
        console.log("===== FixedMath 测试开始 =====");
        
        this.testTrigoAccuracy();
        this.testTrigoPerformance();
        this.testMemoryUsage();
        this.showOptimizationSuggestions();
        
        console.log("===== FixedMath 测试结束 =====");
    }
    
    /**
     * 测试三角函数的精度
     */
    private static testTrigoAccuracy(): void {
        console.log("--- 三角函数精度测试 ---");
        
        // 测试正弦函数
        const testAngles = [0, 30, 45, 60, 90, 120, 180, 270, 360];
        
        console.log("角度\t原生Sin\tFixedSin\t差异\t精度(%)");
        
        for (const angle of testAngles) {
            const radians = angle * Math.PI / 180;
            const nativeSin = Math.sin(radians);
            
            const fixedRadians = FixedMath.toRadians(FixedMath.fromFloat(angle));
            const fixedSin = FixedMath.toFloat(FixedMath.sin(fixedRadians));
            
            const diff = Math.abs(nativeSin - fixedSin);
            const accuracy = diff === 0 ? 100 : (1 - diff / Math.abs(nativeSin)) * 100;
            
            console.log(`${angle}°\t${nativeSin.toFixed(6)}\t${fixedSin.toFixed(6)}\t${diff.toFixed(6)}\t${accuracy.toFixed(2)}%`);
        }
        
        console.log("\n");
    }
    
    /**
     * 测试三角函数的性能
     */
    private static testTrigoPerformance(): void {
        console.log("--- 三角函数性能测试 ---");
        
        const iterations = 1000000; // 100万次调用
        
        // 生成测试角度（0到359度，每隔1度）
        const testAngles: number[] = [];
        for (let i = 0; i < 360; i++) {
            testAngles.push(i);
        }
        
        // 预先转换为弧度值
        const testRadians = testAngles.map(angle => angle * Math.PI / 180);
        const fixedRadians = testAngles.map(angle => 
            FixedMath.toRadians(FixedMath.fromFloat(angle)));
        
        // 测试原生Math.sin性能
        console.log(`测试原生Math.sin (${iterations}次调用)...`);
        const nativeSinStart = performance.now();
        let nativeSinSum = 0;
        
        for (let i = 0; i < iterations; i++) {
            const angleIndex = i % testRadians.length;
            nativeSinSum += Math.sin(testRadians[angleIndex]);
        }
        
        const nativeSinEnd = performance.now();
        const nativeSinTime = nativeSinEnd - nativeSinStart;
        
        // 测试FixedMath.sin性能
        console.log(`测试FixedMath.sin (${iterations}次调用)...`);
        const fixedSinStart = performance.now();
        let fixedSinSum = 0;
        
        for (let i = 0; i < iterations; i++) {
            const angleIndex = i % fixedRadians.length;
            fixedSinSum += FixedMath.toFloat(FixedMath.sin(fixedRadians[angleIndex]));
        }
        
        const fixedSinEnd = performance.now();
        const fixedSinTime = fixedSinEnd - fixedSinStart;
        
        // 测试无插值查表(直接查表)
        console.log(`测试直接查表sin (${iterations}次调用)...`);
        const directLookupStart = performance.now();
        let directLookupSum = 0;
        
        for (let i = 0; i < iterations; i++) {
            const angleIndex = i % testAngles.length;
            directLookupSum += FixedMathLut.SIN_TABLE[angleIndex] / 65536;
        }
        
        const directLookupEnd = performance.now();
        const directLookupTime = directLookupEnd - directLookupStart;
        
        // 测试原生Math.cos性能
        console.log(`测试原生Math.cos (${iterations}次调用)...`);
        const nativeCosStart = performance.now();
        let nativeCosSum = 0;
        
        for (let i = 0; i < iterations; i++) {
            const angleIndex = i % testRadians.length;
            nativeCosSum += Math.cos(testRadians[angleIndex]);
        }
        
        const nativeCosEnd = performance.now();
        const nativeCosTime = nativeCosEnd - nativeCosStart;
        
        // 测试FixedMath.cos性能
        console.log(`测试FixedMath.cos (${iterations}次调用)...`);
        const fixedCosStart = performance.now();
        let fixedCosSum = 0;
        
        for (let i = 0; i < iterations; i++) {
            const angleIndex = i % fixedRadians.length;
            fixedCosSum += FixedMath.toFloat(FixedMath.cos(fixedRadians[angleIndex]));
        }
        
        const fixedCosEnd = performance.now();
        const fixedCosTime = fixedCosEnd - fixedCosStart;
        
        // 测试原生Math.tan性能
        console.log(`测试原生Math.tan (${iterations}次调用)...`);
        const nativeTanStart = performance.now();
        let nativeTanSum = 0;
        
        for (let i = 0; i < iterations; i++) {
            const angleIndex = i % testRadians.length;
            // 跳过90度和270度
            if (testAngles[angleIndex] !== 90 && testAngles[angleIndex] !== 270) {
                nativeTanSum += Math.tan(testRadians[angleIndex]);
            }
        }
        
        const nativeTanEnd = performance.now();
        const nativeTanTime = nativeTanEnd - nativeTanStart;
        
        // 测试FixedMath.tan性能
        console.log(`测试FixedMath.tan (${iterations}次调用)...`);
        const fixedTanStart = performance.now();
        let fixedTanSum = 0;
        
        for (let i = 0; i < iterations; i++) {
            const angleIndex = i % fixedRadians.length;
            // 跳过90度和270度
            if (testAngles[angleIndex] !== 90 && testAngles[angleIndex] !== 270) {
                fixedTanSum += FixedMath.toFloat(FixedMath.tan(fixedRadians[angleIndex]));
            }
        }
        
        const fixedTanEnd = performance.now();
        const fixedTanTime = fixedTanEnd - fixedTanStart;
        
        // 测试原生Math.asin性能
        console.log(`测试原生Math.asin (${iterations}次调用)...`);
        const testValues = [-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1];
        const nativeAsinStart = performance.now();
        let nativeAsinSum = 0;
        
        for (let i = 0; i < iterations; i++) {
            const valueIndex = i % testValues.length;
            nativeAsinSum += Math.asin(testValues[valueIndex]);
        }
        
        const nativeAsinEnd = performance.now();
        const nativeAsinTime = nativeAsinEnd - nativeAsinStart;
        
        // 测试FixedMath.asin性能
        console.log(`测试FixedMath.asin (${iterations}次调用)...`);
        const fixedValues = testValues.map(v => FixedMath.fromFloat(v));
        const fixedAsinStart = performance.now();
        let fixedAsinSum = 0;
        
        for (let i = 0; i < iterations; i++) {
            const valueIndex = i % fixedValues.length;
            fixedAsinSum += FixedMath.toFloat(FixedMath.asin(fixedValues[valueIndex]));
        }
        
        const fixedAsinEnd = performance.now();
        const fixedAsinTime = fixedAsinEnd - fixedAsinStart;
        
        // 测试直接查表asin性能
        console.log(`测试直接查表asin (${iterations}次调用)...`);
        const directAsinStart = performance.now();
        let directAsinSum = 0;
        
        for (let i = 0; i < iterations; i++) {
            const valueIndex = i % testValues.length;
            // 将[-1,1]映射到查表索引
            const value = testValues[valueIndex];
            const index = Math.floor((value + 1) * FixedMathLut.ASIN_TABLE.length / 2);
            const safeIndex = Math.max(0, Math.min(FixedMathLut.ASIN_TABLE.length - 1, index));
            directAsinSum += FixedMathLut.ASIN_TABLE[safeIndex] / 65536;
        }
        
        const directAsinEnd = performance.now();
        const directAsinTime = directAsinEnd - directAsinStart;
        
        // 测试atan2性能
        console.log(`测试原生Math.atan2 (${iterations}次调用)...`);
        const testCoords = [
            {x: 0, y: 1}, {x: 1, y: 1}, {x: 1, y: 0}, 
            {x: 1, y: -1}, {x: 0, y: -1}, {x: -1, y: -1}, 
            {x: -1, y: 0}, {x: -1, y: 1}
        ];
        const nativeAtan2Start = performance.now();
        let nativeAtan2Sum = 0;
        
        for (let i = 0; i < iterations; i++) {
            const coordIndex = i % testCoords.length;
            const coord = testCoords[coordIndex];
            nativeAtan2Sum += Math.atan2(coord.y, coord.x);
        }
        
        const nativeAtan2End = performance.now();
        const nativeAtan2Time = nativeAtan2End - nativeAtan2Start;
        
        // 测试FixedMath.atan2性能
        console.log(`测试FixedMath.atan2 (${iterations}次调用)...`);
        const fixedCoords = testCoords.map(coord => ({
            x: FixedMath.fromFloat(coord.x), 
            y: FixedMath.fromFloat(coord.y)
        }));
        const fixedAtan2Start = performance.now();
        let fixedAtan2Sum = 0;
        
        for (let i = 0; i < iterations; i++) {
            const coordIndex = i % fixedCoords.length;
            const coord = fixedCoords[coordIndex];
            fixedAtan2Sum += FixedMath.toFloat(FixedMath.atan2(coord.y, coord.x));
        }
        
        const fixedAtan2End = performance.now();
        const fixedAtan2Time = fixedAtan2End - fixedAtan2Start;
        
        // 结果输出
        console.log("\n=== 性能比较结果 ===");
        console.log(`函数\t原生Math\tFixedMath\t比率(FixedMath/原生)\t直接查表(如有)`);
        console.log(`sin\t${nativeSinTime.toFixed(2)}ms\t${fixedSinTime.toFixed(2)}ms\t${(fixedSinTime/nativeSinTime).toFixed(2)}x\t${directLookupTime.toFixed(2)}ms (${(directLookupTime/nativeSinTime).toFixed(2)}x)`);
        console.log(`cos\t${nativeCosTime.toFixed(2)}ms\t${fixedCosTime.toFixed(2)}ms\t${(fixedCosTime/nativeCosTime).toFixed(2)}x`);
        console.log(`tan\t${nativeTanTime.toFixed(2)}ms\t${fixedTanTime.toFixed(2)}ms\t${(fixedTanTime/nativeTanTime).toFixed(2)}x`);
        console.log(`asin\t${nativeAsinTime.toFixed(2)}ms\t${fixedAsinTime.toFixed(2)}ms\t${(fixedAsinTime/nativeAsinTime).toFixed(2)}x\t${directAsinTime.toFixed(2)}ms (${(directAsinTime/nativeAsinTime).toFixed(2)}x)`);
        console.log(`atan2\t${nativeAtan2Time.toFixed(2)}ms\t${fixedAtan2Time.toFixed(2)}ms\t${(fixedAtan2Time/nativeAtan2Time).toFixed(2)}x`);
        
        // 测试点乘计算角度性能
        console.log(`\n--- 向量角度计算性能测试 ---`);
        
        // 生成测试向量对
        const vectorPairs = [];
        for (let i = 0; i < 36; i++) {
            const angle1 = i * 10 * Math.PI / 180;
            for (let j = 0; j < 36; j++) {
                const angle2 = j * 10 * Math.PI / 180;
                vectorPairs.push({
                    v1: { x: Math.cos(angle1), y: Math.sin(angle1) },
                    v2: { x: Math.cos(angle2), y: Math.sin(angle2) }
                });
            }
        }
        
        // 测试原生Math方法
        console.log(`测试原生Math计算向量角度 (${iterations}次调用)...`);
        const nativeVectorStart = performance.now();
        let nativeAngleSum = 0;
        
        for (let i = 0; i < iterations; i++) {
            const pairIndex = i % vectorPairs.length;
            const pair = vectorPairs[pairIndex];
            const dot = pair.v1.x * pair.v2.x + pair.v1.y * pair.v2.y;
            nativeAngleSum += Math.acos(dot);
        }
        
        const nativeVectorEnd = performance.now();
        const nativeVectorTime = nativeVectorEnd - nativeVectorStart;
        
        // 测试FixedMath.acos方法
        console.log(`测试FixedMath.acos计算向量角度 (${iterations}次调用)...`);
        const fixedVectorStart = performance.now();
        let fixedAngleSum = 0;
        
        for (let i = 0; i < iterations; i++) {
            const pairIndex = i % vectorPairs.length;
            const pair = vectorPairs[pairIndex];
            const x1 = FixedMath.fromFloat(pair.v1.x);
            const y1 = FixedMath.fromFloat(pair.v1.y);
            const x2 = FixedMath.fromFloat(pair.v2.x);
            const y2 = FixedMath.fromFloat(pair.v2.y);
            
            const dot = FixedMath.add(FixedMath.mul(x1, x2), FixedMath.mul(y1, y2));
            fixedAngleSum += FixedMath.toFloat(FixedMath.acos(dot));
        }
        
        const fixedVectorEnd = performance.now();
        const fixedVectorTime = fixedVectorEnd - fixedVectorStart;
        
        // 测试FixedMath专用方法
        console.log(`测试FixedMath.angleBetweenVectors (${iterations}次调用)...`);
        const specializedStart = performance.now();
        let specializedAngleSum = 0;
        
        for (let i = 0; i < iterations; i++) {
            const pairIndex = i % vectorPairs.length;
            const pair = vectorPairs[pairIndex];
            const x1 = FixedMath.fromFloat(pair.v1.x);
            const y1 = FixedMath.fromFloat(pair.v1.y);
            const x2 = FixedMath.fromFloat(pair.v2.x);
            const y2 = FixedMath.fromFloat(pair.v2.y);
            
            specializedAngleSum += FixedMath.toFloat(FixedMath.angleBetweenVectors(x1, y1, x2, y2));
        }
        
        const specializedEnd = performance.now();
        const specializedTime = specializedEnd - specializedStart;
        
        // 测试FixedMath点乘专用方法
        console.log(`测试FixedMath.angleFromDot (${iterations}次调用)...`);
        const dotMethodStart = performance.now();
        let dotMethodAngleSum = 0;
        
        for (let i = 0; i < iterations; i++) {
            const pairIndex = i % vectorPairs.length;
            const pair = vectorPairs[pairIndex];
            const dot = FixedMath.fromFloat(pair.v1.x * pair.v2.x + pair.v1.y * pair.v2.y);
            dotMethodAngleSum += FixedMath.toFloat(FixedMath.angleFromDot(dot));
        }
        
        const dotMethodEnd = performance.now();
        const dotMethodTime = dotMethodEnd - dotMethodStart;
        
        // 输出向量角度计算测试结果
        console.log(`\n=== 向量角度计算性能比较 ===`);
        console.log(`方法\t\t\t时间\t\t比原生方法`);
        console.log(`原生Math.acos\t\t${nativeVectorTime.toFixed(2)}ms\t1.00x`);
        console.log(`FixedMath.acos\t\t${fixedVectorTime.toFixed(2)}ms\t${(fixedVectorTime/nativeVectorTime).toFixed(2)}x`);
        console.log(`FixedMath.angleFromDot\t${dotMethodTime.toFixed(2)}ms\t${(dotMethodTime/nativeVectorTime).toFixed(2)}x`);
        console.log(`FixedMath.angleBetweenVectors\t${specializedTime.toFixed(2)}ms\t${(specializedTime/nativeVectorTime).toFixed(2)}x`);
        
        // 对最快的方法提供建议
        let fastestMethod = "原生Math.acos";
        let fastestTime = nativeVectorTime;
        
        if (fixedVectorTime < fastestTime) {
            fastestMethod = "FixedMath.acos";
            fastestTime = fixedVectorTime;
        }
        if (dotMethodTime < fastestTime) {
            fastestMethod = "FixedMath.angleFromDot";
            fastestTime = dotMethodTime;
        }
        if (specializedTime < fastestTime) {
            fastestMethod = "FixedMath.angleBetweenVectors";
        }
        
        console.log(`\n向量角度计算推荐: 使用 ${fastestMethod} 方法可获得最佳性能`);
        
        const avgSpeedup = (
            (fixedSinTime/nativeSinTime) + 
            (fixedCosTime/nativeCosTime) + 
            (fixedTanTime/nativeTanTime) +
            (fixedAsinTime/nativeAsinTime) +
            (fixedAtan2Time/nativeAtan2Time)
        ) / 5;
        
        console.log(`\n平均速度比: ${avgSpeedup.toFixed(2)}x`);
        
        // 给出解读
        if (avgSpeedup < 1) {
            console.log("结论: FixedMath三角函数平均性能优于原生Math函数");
        } else if (avgSpeedup < 1.5) {
            console.log("结论: FixedMath三角函数性能略微慢于原生Math函数，但差距较小");
        } else if (avgSpeedup < 3) {
            console.log("结论: FixedMath三角函数性能慢于原生Math函数，但对大多数游戏场景仍然足够");
        } else {
            console.log("结论: FixedMath三角函数性能明显慢于原生Math函数，可能需要优化或减少调用");
        }
        
        // 对比直接查表与带插值
        if (directLookupTime < fixedSinTime) {
            const improvement = ((fixedSinTime - directLookupTime) / fixedSinTime * 100).toFixed(2);
            console.log(`优化提示: 直接查表sin比带线性插值的实现快 ${improvement}%，对于精度要求不高的场景可考虑直接查表`);
        }
        
        if (directAsinTime < fixedAsinTime) {
            const improvement = ((fixedAsinTime - directAsinTime) / fixedAsinTime * 100).toFixed(2);
            console.log(`优化提示: 直接查表asin比带线性插值的实现快 ${improvement}%，考虑使用更简单的查表算法`);
        }
        
        // 特别关注asin的性能问题
        if (fixedAsinTime/nativeAsinTime > 3) {
            console.log("\n特别注意: asin函数性能显著低于其他三角函数，建议优先优化");
            console.log("- 考虑减少asin的查表精度换取性能");
            console.log("- 检查是否可以用其他数学方法替代asin (例如使用向量点积计算角度)");
            console.log("- 如果游戏逻辑频繁使用asin，考虑重构相关算法");
        }
    }

    /**
     * 测试内存占用
     */
    private static testMemoryUsage(): void {
        console.log("\n--- 内存占用分析 ---");
        
        // 计算查找表大致内存占用
        const sinTableSize = FixedMathLut.SIN_TABLE.length * 4; // 每个数字约4字节
        const tanTableSize = FixedMathLut.TAN_TABLE.length * 4;
        const asinTableSize = FixedMathLut.ASIN_TABLE.length * 4;
        const acosTableSize = FixedMathLut.ACOS_TABLE.length * 4;
        const constantsSize = Object.keys(FixedMathLut.CONSTANTS).length * 4;
        
        const totalSize = sinTableSize + tanTableSize + asinTableSize + acosTableSize + constantsSize;
        
        console.log(`SIN_TABLE: ${sinTableSize} 字节 (${FixedMathLut.SIN_TABLE.length} 项)`);
        console.log(`TAN_TABLE: ${tanTableSize} 字节 (${FixedMathLut.TAN_TABLE.length} 项)`);
        console.log(`ASIN_TABLE: ${asinTableSize} 字节 (${FixedMathLut.ASIN_TABLE.length} 项)`);
        console.log(`ACOS_TABLE: ${acosTableSize} 字节 (${FixedMathLut.ACOS_TABLE.length} 项)`);
        console.log(`常量: ${constantsSize} 字节`);
        console.log(`总计: ${totalSize} 字节 (约 ${(totalSize / 1024).toFixed(2)} KB)`);
        
        if (totalSize < 50 * 1024) {
            console.log("内存占用结论: 内存占用很小，在现代设备上可以忽略不计");
        } else if (totalSize < 200 * 1024) {
            console.log("内存占用结论: 内存占用适中，对大多数游戏场景都是可接受的");
        } else {
            console.log("内存占用结论: 内存占用较大，可能需要考虑减少表大小或压缩数据");
        }
    }
    
    /**
     * 显示优化建议
     */
    private static showOptimizationSuggestions(): void {
        console.log("\n--- 性能优化建议 ---");
        console.log("1. 热点路径优化: 对于频繁调用的三角函数，考虑以下优化:");
        console.log("   - 预计算: 如果角度是固定或有限集合，提前计算结果并缓存");
        console.log("   - 减少精度: 对于视觉效果不敏感的计算，可以使用更粗糙的查找表或简化计算");
        console.log("   - 近似计算: 对某些场景可使用多项式近似代替完整计算(比如小角度sin(x)≈x)");
        
        console.log("\n2. 内存占用优化:");
        console.log("   - 减少表大小: 可以减少采样点，对于不常用角度使用线性插值");
        console.log("   - 压缩数据: 可以使用16位或8位整数存储查找表，使用时再转换成定点数");
        console.log("   - 对称性利用: 利用三角函数的对称性，只存储0-90度的数据");
        
        console.log("\n3. 特定应用场景优化:");
        console.log("   - 2D旋转: 可以使用预计算的旋转矩阵");
        console.log("   - 物理模拟: 考虑用查表+插值替代频繁调用的三角函数");
        console.log("   - 粒子系统: 对大量粒子使用简化计算");
        
        console.log("\n4. 权衡精度与性能:");
        console.log("   - 对于视觉表现: 可以牺牲一些精度换取性能");
        console.log("   - 对于物理模拟: 需要保持较高精度以避免累积误差");
        console.log("   - 对于帧同步: 确定性比精度和性能更重要");
        
        console.log("\n5. 点乘角度计算优化:");
        console.log("   - 使用直接查表的acos代替插值计算能显著提高性能");
        console.log("   - 对于常见场景(垂直、平行向量)使用特殊优化");
        console.log("   - 使用专用的angleFromDot函数处理已有点乘结果");
        console.log("   - 对于归一化的向量，可以直接使用点乘结果，避免重复计算");
        console.log("   - 在角度值附近采用更密集的查表值以提高精度");
    }
}

// 如果直接运行此文件则执行测试
if (typeof window !== 'undefined' && window.document) {
    window.addEventListener('DOMContentLoaded', () => {
        FixedMathTest.runAllTests();
    });
} else {
    // Node.js环境
    FixedMathTest.runAllTests();
}


